package red.uec.service.impl;

import com.baomidou.mybatisplus.mapper.Condition;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.starchain.sdk.Account;
import com.starchain.sdk.AccountAsset;
import com.starchain.sdk.SendTransfer;
import com.starchain.sdk.Transaction;
import com.starchain.sdk.cryptography.Base58;
import com.starchain.sdk.info.AssetInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.digest.MessageDigestAlgorithms;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import red.uec.entity.User;
import red.uec.entity.UserAsset;
import red.uec.entity.UserAssetTransactionRecord;
import red.uec.exception.BusinessException;
import red.uec.exception.ExceptionEnum;
import red.uec.jwt.JwtHelper;
import red.uec.mapper.UserAssetMapper;
import red.uec.model.*;
import red.uec.service.UserAssetService;
import red.uec.service.UserAssetTransactionRecordService;
import red.uec.service.UserService;
import red.uec.util.IdGenerator;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

/**
 * <p>
 * 用户资产表 服务实现类
 * </p>
 *
 * @author 谭剑晖
 * @since 2018-11-13
 */
@Service
@Slf4j
public class UserAssetServiceImpl extends ServiceImpl<UserAssetMapper, UserAsset> implements UserAssetService {
    @Autowired
    private JwtHelper jwtHelper;

    @Autowired
    private UserService userService;

    @Autowired
    private UserAssetTransactionRecordService userAssetTransactionRecordService;

    @Transactional(rollbackFor = {Exception.class}, propagation = Propagation.REQUIRES_NEW)
    public boolean insertRN(UserAsset userAsset){
        return this.insert(userAsset);
    }

    @Transactional(rollbackFor = {Exception.class})
    public UserAssetRegisterResponse register(UserAssetRegisterRequest request){
        String userId = JwtHelper.getUserId();

        // 新增资产记录
        UserAsset userAsset = new UserAsset();
        userAsset.setId(IdGenerator.newId());
        userAsset.setUserId(userId);
        userAsset.setAssetNickName(request.getAssetNickName());
        userAsset.setEnergyTypeId(request.getEnergyTypeId());
        userAsset.setEnergyTypeName(request.getEnergyTypeName());
        userAsset.setDataSourceId(request.getDataSourceId());
        userAsset.setDataSourceName(request.getDataSourceName());
        userAsset.setProjectAddress(request.getProjectAddress());
        userAsset.setProjectAddressLongitude(request.getProjectAddressLongitude());
        userAsset.setProjectAddressLatitude(request.getProjectAddressLatitude());
        userAsset.setDeviceCapacity(request.getDeviceCapacity());
        userAsset.setInverterSequence(request.getInverterSequence());
        userAsset.setAssemblyBrandSpec(request.getAssemblyBrandSpec());
        userAsset.setAssetJson(getAssetJson(userAsset));
        userAsset.setAssetJsonHash(new DigestUtils(MessageDigestAlgorithms.SHA_256).digestAsHex(userAsset.getAssetJson()));
        userAsset.setChainTransactionId("NA");
        userAsset.setCreateTime(new Date());
        userAsset.setCreator(userId);
        userAsset.setModifyTime(userAsset.getCreateTime());
        userAsset.setModifier(userId);
        this.insert(userAsset);

        // 用户资产信息交易记录登记
        UserAssetTransactionRecord userAssetTransactionRecord = new UserAssetTransactionRecord();
        userAssetTransactionRecord.setId(IdGenerator.newId());
        userAssetTransactionRecord.setUserAssetId(userAsset.getId());
        userAssetTransactionRecord.setTransactionSummary("新登记资产");
        userAssetTransactionRecord.setCreateTime(userAsset.getCreateTime());
        userAssetTransactionRecord.setCreator(userId);
        userAssetTransactionRecord.setModifyTime(userAsset.getCreateTime());
        userAssetTransactionRecord.setModifier(userId);
        userAssetTransactionRecordService.insert(userAssetTransactionRecord);

        // 资产信息上链
        byte[] privateKey = Base58.decode("6ExjudHPCeDG6pBrrHXeX56siEC4SQnnXRic8eCLyJTN");
        Account account = new Account(privateKey);
        AssetInfo[] assetList = AccountAsset.getUpspent("http://47.52.44.156:25884", account);
        AssetInfo aInfo = null;
        for(AssetInfo assetInfo : assetList){
            if (assetInfo.getAssetId().equals("e4aa1318a3b5b5e3949520daaead3b9e378414ded20a89b0f25561ba9203aac5")) {
                aInfo = assetInfo;
            }
        }
        String txData = Transaction.makeTransferTransaction(aInfo,
                account.publicKeyEncoded, "SPAVe4snCDztuvX6ZUCcAAUUVqfc8ThUH7",
                0.00000001D,
                userAsset.getAssetJsonHash());
        String txIdString = SendTransfer.SignTxAndSend("http://47.52.44.156:25884", txData, account.publicKeyEncoded, privateKey);
        log.info(txIdString);

        return new UserAssetRegisterResponse().success();
    }

    public UserAssetQueryResponse query(UserAssetQueryRequest userAssetQueryRequest){
        String userId = JwtHelper.getUserId();

        // 获取用户资产列表
        List<UserAsset> userAssetList =  this.selectList(Condition.create()
                .setSqlSelect("*")
                .eq("user_id", userId));
        List<UserAssetModel> userAssetModelList = new ArrayList<>();

        // 获取用户资产交易记录列表
        List<UserAssetTransactionRecord> userAssetTransactionRecordList
                = userAssetTransactionRecordService.selectList(
                        Condition.create().setSqlSelect("*")
                                .where("user_asset_id in (select id from user_asset where user_id = {0})", userId)
                                .orderBy(" user_asset_id, create_time ")
        );

        for(UserAsset userAsset : userAssetList){
            UserAssetModel userAssetModel = new UserAssetModel();
            userAssetModel.setId(userAsset.getId());
            userAssetModel.setUserId(userAsset.getUserId());
            userAssetModel.setAssetNickName(userAsset.getAssetNickName());
            userAssetModel.setEnergyTypeId(userAsset.getEnergyTypeId());
            userAssetModel.setEnergyTypeName(userAsset.getEnergyTypeName());
            userAssetModel.setDataSourceId(userAsset.getDataSourceId());
            userAssetModel.setDataSourceName(userAsset.getDataSourceName());
            userAssetModel.setProjectAddress(userAsset.getProjectAddress());
            userAssetModel.setProjectAddressLongitude(userAsset.getProjectAddressLongitude());
            userAssetModel.setProjectAddressLatitude(userAsset.getProjectAddressLatitude());
            userAssetModel.setDeviceCapacity(userAsset.getDeviceCapacity());
            userAssetModel.setInverterSequence(userAsset.getInverterSequence());
            userAssetModel.setAssemblyBrandSpec(userAsset.getAssemblyBrandSpec());

            userAssetModel.setDailyAveragePower(this.calcDailyAveragePower(userAsset.getDeviceCapacity()));
            userAssetModel.setTotalGeneratingCapacity(this.calcTotalGeneratingCapacity(userAsset.getCreateTime(), userAssetModel.getDailyAveragePower()));
            userAssetModel.setAssetValuation(this.calcAssetValuation(userAssetModel.getDailyAveragePower()));

            // 设置用户资产交易记录
            if(null != userAssetTransactionRecordList && !userAssetTransactionRecordList.isEmpty()){
                List<UserAssetTransactionRecordModel> userAssetTransactionRecordModelList = new ArrayList<>();
                for(UserAssetTransactionRecord userAssetTransactionRecord : userAssetTransactionRecordList){
                    if(StringUtils.equals(userAssetModel.getId(), userAssetTransactionRecord.getUserAssetId())) {
                        UserAssetTransactionRecordModel userAssetTransactionRecordModel
                                = new UserAssetTransactionRecordModel();
                        userAssetTransactionRecordModel.setId(userAssetTransactionRecord.getId());
                        userAssetTransactionRecordModel.setTransactionSummary(userAssetTransactionRecord.getTransactionSummary());
                        userAssetTransactionRecordModel.setUserAssetId(userAssetTransactionRecord.getUserAssetId());
                        userAssetTransactionRecordModelList.add(userAssetTransactionRecordModel);
                        userAssetModel.setUserAssetTransactionRecordModelList(userAssetTransactionRecordModelList);
                    }
                }
            }

            userAssetModelList.add(userAssetModel);
        }
        return new UserAssetQueryResponse(userAssetModelList).success();
    }

    @Transactional(rollbackFor = {Exception.class})
    public UserAssetTransferResponse transfer(UserAssetTransferRequest userAssetTransferRequest){
        String fromUserId = JwtHelper.getUserId();
        String toUserId = userAssetTransferRequest.getToUserId();

        if(StringUtils.equals(fromUserId, toUserId)){
            throw new BusinessException("E001","不能将资产转给本人");
        }

        // 获取资产对象
        UserAsset userAsset = this.selectById(userAssetTransferRequest.getUserAssetId());
        if(userAsset == null || !StringUtils.equals(fromUserId, userAsset.getUserId())){
            throw new BusinessException("E001","无效资产");
        }

        // 检查被转让资产用户是否存在
        User user = userService.selectById(toUserId);
        if(null == user){
            throw new BusinessException("E001","无效用户");
        }

        userAsset.setUserId(toUserId);
        userAsset.setModifier(fromUserId);
        userAsset.setModifyTime(new Date());
        this.updateById(userAsset);

        // 用户资产信息交易记录登记
        UserAssetTransactionRecord userAssetTransactionRecord = new UserAssetTransactionRecord();
        userAssetTransactionRecord.setId(IdGenerator.newId());
        userAssetTransactionRecord.setUserAssetId(userAsset.getId());
        userAssetTransactionRecord.setTransactionSummary("资产转让：" + fromUserId + " 转给 " + toUserId);
        userAssetTransactionRecord.setCreateTime(userAsset.getModifyTime());
        userAssetTransactionRecord.setCreator(fromUserId);
        userAssetTransactionRecord.setModifyTime(userAsset.getModifyTime());
        userAssetTransactionRecord.setModifier(fromUserId);
        userAssetTransactionRecordService.insert(userAssetTransactionRecord);

        return new UserAssetTransferResponse().success();
    }

    private String getAssetJson(UserAsset userAsset){
        Map<String, Object> map = new TreeMap<>();

        map.put("assetNickName",userAsset.getAssetNickName());
        map.put("energyTypeId",userAsset.getEnergyTypeId());
        map.put("energyTypeName",userAsset.getEnergyTypeName());
        map.put("dataSourceId",userAsset.getDataSourceId());
        map.put("dataSourceName",userAsset.getDataSourceName());
        map.put("projectAddress",userAsset.getProjectAddress());
        map.put("projectAddressLongitude",userAsset.getProjectAddressLongitude());
        map.put("projectAddressLatitude",userAsset.getProjectAddressLatitude());
        map.put("projectAddressLatitude",userAsset.getProjectAddressLatitude());
        map.put("deviceCapacity",userAsset.getDeviceCapacity());
        map.put("inverterSequence",userAsset.getInverterSequence());
        map.put("assemblyBrandSpec",userAsset.getAssemblyBrandSpec());

        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(map);
        }catch (JsonProcessingException e){
            throw new BusinessException(ExceptionEnum.err_system_busy.name(), "系统繁忙，请稍候再试！", e);
        }
    }

    /**
     * 计算日均功率
     * 日均功率=装机容量x3.6
     *
     * @param deviceCapacity 装机容量
     * @return
     */
    private BigDecimal calcDailyAveragePower(BigDecimal deviceCapacity){
        return deviceCapacity.multiply(new BigDecimal("3.6")).setScale(2, RoundingMode.HALF_UP);
    }

    /**
     * 计算累计发电量
     * 累计电量=日均功率x累计天数
     *
     * @param registerDate 资产登记日期
     * @param dailyAveragePower 日均功率
     * @return
     */
    private BigDecimal calcTotalGeneratingCapacity(Date registerDate, BigDecimal dailyAveragePower){
        long days = (System.currentTimeMillis() - registerDate.getTime()) / 86400000L;
        return dailyAveragePower.multiply(new BigDecimal(days)).setScale(2, RoundingMode.HALF_UP);
    }

    /**
     * 计算资产估值
     * 资产估值=日均功率x365天x20年x（0.6元+0.32）
     *
     * @param dailyAveragePower 日均功率
     * @return
     */
    private BigDecimal calcAssetValuation(BigDecimal dailyAveragePower){
        return dailyAveragePower.multiply(new BigDecimal("6716")).setScale(2, RoundingMode.HALF_UP);
    }
}
